import os,time
import argparse
import platform
import shutil
cur_path = os.getcwd()
rec_flag = False # 是否遍历子目录
backupflg= False # 是否备份源目标
overwrite = False # 冲突时是否覆盖
numberup = '' # 数字递增格式
folderflag = False # 文件操作/文件夹操作
list=[]
########################################################
### 批量将‘文件夹’名称变为小写/大写(lower/upper)
########################################################
def rename_folders_to_lowercase(directory,lower=True,recursive=True):
    # for foldername in os.listdir(directory):
        # if os.path.isdir(os.path.join(directory, foldername)):
            # new_foldername = foldername.lower()
            # rename_path(directory,foldername,new_foldername)  
    file_list=[('路径','源目标','操作后','类型','操作')]
    type ='文件夹'
    operation='小写格式化' if lower else '大写格式化'
    for root, dirs, files in os.walk(directory):
        for dir in dirs:
            new_foldername = dir.lower() if lower else dir.upper()
            file_list.append((root,dir,new_foldername,type,operation))
            #rename_path(root,dir,new_foldername,overwriteflag,backupflag)
        if not recursive:# 默认值为True，表示默认情况下会递归遍历子目录
            break
    return file_list
########################################################
### 批量将当前目录（directory）文件夹某个字段(str)替换/去掉
########################################################
def replace_chars_from_foldersname(directory,str,sub='',recursive=True):
    file_list=[('路径','源目标','操作后','类型','操作')]
    type ='文件夹'
    operation='替换匹配字符' if len(sub)!=0 else '删除匹配字符'
    for root, dirs, files in os.walk(directory):
        for dir in dirs:
            new_foldername = dir.replace(str,sub)# 替换字符
            if new_foldername != dir:
                file_list.append((root,dir,new_foldername,type,operation))
        if not recursive:# 默认值为True，表示默认情况下会递归遍历子目录
            break
    return file_list
########################################################
### 批量将当前目录（directory）文件夹添加某个字段(str)
### position: true-在尾部添加 / false-在头部添加
########################################################
def append_chars_to_foldersname(directory,str,position=True,recursive=True,numb=''):
    file_list=[('路径','源目标','操作后','类型','操作')]
    type ='文件夹'
    operation='尾部追加字符' if position else '头部追加字符'
    if len(numb)!=0:
        width = len(numb)
        number = int(numb) if int(numb)!=None else 1
    for root, dirs, files in os.walk(directory):
        for dir in dirs:
            if len(numb)!=0:
                fmt_num = f"{number:0{width}d}"
                char = (str+fmt_num) if position else (fmt_num+str)
                number = number + 1
            else:
                char = str
            new_foldername = (dir+char) if position else (char+dir)
            file_list.append((root,dir,new_foldername,type,operation))
        if not recursive:# 默认值为True，表示默认情况下会递归遍历子目录
            break
    return file_list
########################################################
### 批量将‘文件’名称变为小写/大写(lower/upper)
########################################################
def rename_file_to_lowercase(directory,lower=True,recursive=True):
    # for filename in os.listdir(directory):
        # if os.path.isfile(os.path.join(directory, filename)):
            # base, ext = os.path.splitext(filename)  # 分割文件名和扩展名
            # new_filename = base.lower()+ext
            # rename_path(directory,filename,new_filename)
    file_list=[('路径','源目标','操作后','类型','操作')]
    type ='文件'
    operation='小写格式化' if lower else '大写格式化'
    for root, dirs, files in os.walk(directory):
        for file in files:
            base, ext = os.path.splitext(file)  # 分割文件名和扩展名
            new_filename = base.lower()+ext if lower else base.upper()+ext
            file_list.append((root,file,new_filename,type,operation))
        if not recursive:# 默认值为True，表示默认情况下会递归遍历子目录
            break
    return file_list
########################################################
### 批量将当前目录（directory）文件名某个字段(str)替换
########################################################
def replace_chars_from_filename(directory,str,sub='', recursive=True):
    # for filename in os.listdir(directory):
        # if os.path.isfile(os.path.join(directory, filename)):
            # base, ext = os.path.splitext(filename)  # 分割文件名和扩展名
            # new_filename = base.replace(str,sub)+ext
            # rename_path(directory,filename,new_filename)
    file_list=[('路径','源目标','操作后','类型','操作')]
    type ='文件'
    operation='替换匹配字符' if len(sub)!=0 else '删除匹配字符'
    for root, dirs, files in os.walk(directory):
        for file in files:
            base, ext = os.path.splitext(file)  # 分割文件名和扩展名
            new_filename = base.replace(str,sub)+ext
            if new_filename != file:
                file_list.append((root,file,new_filename,type,operation))
        if not recursive:# 默认值为True，表示默认情况下会递归遍历子目录
            break
    return file_list 
########################################################
### 批量将当前目录（directory）文件名添加某个字段(str)
### position: true-在尾部添加 / false-在头部添加
########################################################
def append_chars_to_filename(directory,str,position=True, recursive=True,numb=''):
    file_list=[('路径','源目标','操作后','类型','操作')]
    type ='文件'
    operation='尾部追加字符' if position else '头部追加字符'
    if len(numb)!=0:
        width = len(numb)
        number = int(numb) if int(numb)!=None else 1
    for root, dirs, files in os.walk(directory):
        for file in files:
            if len(numb)!=0:
                fmt_num = f"{number:0{width}d}"
                char = (str+fmt_num) if position else (fmt_num+str)
                number = number + 1
            else:
                char = str
            base, ext = os.path.splitext(file)  # 分割文件名和扩展名
            new_filename = (base+char+ext) if position else (char+base+ext)
            file_list.append((root,file,new_filename,type,operation))
        if not recursive:# 默认值为True，表示默认情况下会递归遍历子目录
            break
    return file_list
########################################################
##  替换文件后缀名（文件扩展名），只更改指定的文件后缀
##  替换字符可以为空字符，则为去掉文件后缀名（文件扩展名）
########################################################
def replace_ext_from_filename(directory,tar_ext,new_ext='', recursive=True):
    file_list=[('路径','源目标','操作后','类型','操作')]
    type ='文件'
    operation='替换文件后缀' if (len(new_ext)!=0) else '删除文件后缀'
    for root, dirs, files in os.walk(directory):
        for file in files:
            # 添加后缀到文件名中（不包括扩展名）
            base, ext = os.path.splitext(file)  # 分割文件名和扩展名
            if tar_ext == ext:
                new_filename = base + new_ext  # 更改后缀，添加新的后缀
                file_list.append((root,file,new_filename,type,operation))
        if not recursive:# 默认值为True，表示默认情况下会递归遍历子目录
            break
    return file_list
########################################################
##  给文件强制添加文件后缀名（文件扩展名）
########################################################
def append_ext_to_filename(directory,tar_ext, recursive=True):
    file_list=[('路径','源目标','操作后','类型','操作')]
    type ='文件'
    operation='强制更改后缀'
    for root, dirs, files in os.walk(directory):
        for file in files:
            # 添加后缀到文件名中（不包括扩展名）
            base, ext = os.path.splitext(file)  # 分割文件名和扩展名
            if tar_ext != ext:
                new_filename = base + tar_ext # 去掉文件后缀名（文件扩展名）
                file_list.append((root,file,new_filename,type,operation))
        if not recursive:# 默认值为True，表示默认情况下会递归遍历子目录
            break
    return file_list
###########################################################
##  安全的重命名文件/文件夹
###########################################################
def renamed(directory,old_filename,new_filename,overwriteflag = False ,backupflag= False):
    if new_filename != old_filename and os.path.exists(directory):# 构建操作后名的完整路径，重命名文件
        source = os.path.join(directory, old_filename)
        target = os.path.join(directory, new_filename)
    else:
        return
    fmtstr = "Renamed >> "+old_filename.ljust(25)+'to  >> '+new_filename.ljust(25)

    print(fmtstr)
    isFile = False
    if os.path.isfile(source) and backupflag:
        isFile = True
        path_b = directory+'\\backup\\'
        path_o = directory+'\\overwrite\\'
        backup = os.path.join(path_b, old_filename)
        backta = os.path.join(path_o, new_filename)
        print(f'Backup:{path_b}{backup}')
    # 检查旧文件是否存在  
    # if not os.path.exists(source):  
        # my_print(f"Error: {old_filename} does not exist.")  
        # return
    # 检查操作后是否已存在，并询问是否覆盖（这里只是示例，你可能希望自动处理）  
    if platform.system() != "Windows" and os.path.exists(target):  
        print(f"Warning: {target},New file already exists. Overwrite? (y/n)")  
        if overwriteflag == True and isFile == True:
            if not os.path.exists(path_o):
                os.makedirs(path_o)
            shutil.copy2(target, backta) 
            my_print(f"Overwrite files backup in: {backta}")
            os.remove(target)  # 注意：这可能会引发异常，应该在try-except块中处理
        elif overwriteflag == False and isFile == True:
            return
        else:
            user_input = input().strip().lower()  # 使用 strip() 去除首尾空白 
            valid_inputs = ('yes', 'y')  
            if user_input in valid_inputs: # 如果选择覆盖，可以删除现有文件或更改操作后名
                if not os.path.exists(path_o):
                    os.makedirs(path_o)
                shutil.copy2(target, backta) 
                my_print(f"Overwrite files backup in: {backta}")
                os.remove(target)  # 注意：这可能会引发异常，应该在try-except块中处理 
            else:
                print("Aborting file rename.")  
                return
    try:  
        # 尝试重命名文件  
        if (backupflag and isFile):
            if not os.path.exists(path_b):
                os.makedirs(path_b)
            shutil.copy2(source, backup)# 备份文件
            my_print(f"Renamed files backup in: {backup}")
        os.rename(source, target)  
        my_print(f"Renamed '{source}' to '{target}'")
    except OSError as e:  
        # 处理可能的异常，如文件被其他进程锁定  
        my_print(f"Error occurred while renaming the file: {e}")

##  定义一个类似于print的函数，但是使用文件log来记录日志信息  
# def my_print(*args):  
    # mylog = open('logger.log', mode = 'a',encoding='utf-8')
    # current_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())  
    # message = ' '.join(map(str, args))  # 将args转换为字符串并连接  
    # print(f'{current_time} - {message}',file=mylog)  # 使用logger记录信息 
    # mylog.close()
# 日志记录函数
def my_print(*args):
    with open('logger.log', 'a', encoding='utf-8') as log_file:
        current_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
        message = ' '.join(map(str, args))  # 将args转换为字符串并连接
        log_file.write(f"{current_time} - {message}\n")
if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="这是一个批量操作工具，支持文件名大小写批量变换、文件(File)/文件夹(Folder)名称批量修改、文件后缀批量更改等")
    parser.add_argument("-v", action="store_true", help="显示版本信息")
    parser.add_argument("--case", type=str,metavar='lower/upper',choices=['lower','upper'],help="当前目录下，将所有文件名称变为大/小写字母: `python example.py -c lower`")
    parser.add_argument("--replace", type=str,metavar=('str','sub'),nargs ='*',help="替换字符(将指定的字符串替换为新的字符[替换字符可以为空字符]，文件类型不变), `python example.py -r _str`")
    parser.add_argument("--append", type=str,metavar='string',help="添加字符(当前目录下，将所有文件名结尾添加指定字符串，文件类型不变), `python example.py -a _str`")
    parser.add_argument("--exchange", type=str,metavar=('source','target'),nargs ='*',help="更改目标文件的文件后缀(当前目录下，目标文件将被更改后缀，文件类型改变): `python example.py -e .txt .cpp`")
    parser.add_argument("--suffix", type=str,metavar=('suffix'),help="更改所有文件的文件后缀(当前目录下，所有文件将被更改后缀，文件类型改变): `python example.py -s .txt`")
    parser.add_argument("--path", type=str,metavar='path',help="指定工作路径: `python example.py -c lower -p D:\\WorkerDir`")
    parser.add_argument("--recu",action="store_true",help="递归遍历子目录: `python example.py -f lower -u`")
    parser.add_argument("--backup",action="store_true",help="自动备份文件: `python example.py -f lower -b`")
    parser.add_argument("--overwrite",action="store_true",help="冲突时覆盖: `python example.py -f lower -o`")
    parser.add_argument("--number",type=str,metavar='string',help="添加字符时,数字递增格式,缺省时禁用数字递增: `python example.py -a _ -n 001`")
    parser.add_argument("--folder",action="store_true",help="字符操作时,指定目标是文件夹,缺省时目标是文件: `python example.py -a Folder -f`")
    parser.add_argument("-c", type=str,metavar='lower/upper',choices=['lower','upper'],help="当前目录下，将所有文件名称变为大/小写字母: `python example.py -c lower`")
    parser.add_argument("-r", type=str,metavar=('str','sub'),nargs ='*',help="替换字符(将指定的字符串替换为新的字符[替换字符可以为空字符]，文件类型不变), `python example.py -r _str`")
    parser.add_argument("-a", type=str,metavar='string',help="添加字符(当前目录下，将所有文件名结尾添加指定字符串，文件类型不变), `python example.py -a _str`")
    parser.add_argument("-e", type=str,metavar=('source','target'),nargs ='*',help="更改目标文件的文件后缀(当前目录下，目标文件将被更改后缀，文件类型改变): `python example.py -e .txt .cpp`")
    parser.add_argument("-s", type=str,metavar=('suffix'),help="更改所有文件的文件后缀(当前目录下，所有文件将被更改后缀，文件类型改变): `python example.py -s .txt`")
    parser.add_argument("-p", type=str,metavar='path',help="指定工作路径: `python example.py -c lower -p D:\\WorkerDir`")
    parser.add_argument("-u",action="store_true",help="递归遍历子目录: `python example.py -f lower -u`")
    parser.add_argument("-b",action="store_true",help="自动备份文件: `python example.py -f lower -b`")
    parser.add_argument("-o",action="store_true",help="冲突时覆盖: `python example.py -f lower -o`")
    parser.add_argument("-n",type=str,metavar='string',help="添加字符时,数字递增格式,缺省时禁用数字递增: `python example.py -a _ -n 001`")
    parser.add_argument("-f",action="store_true",help="字符操作时,指定目标是文件夹,缺省时目标是文件: `python example.py -a Folder -f`")
    args = parser.parse_args()    # 解析命令行参数
    
    if args.p or args.path:# 设置工作目录
        path = args.p if len(args.p)!=0 else args.path 
        if os.path.exists(path):
            cur_path=path
        else:
            print("您指定的路径无效,程序退出")
            import sys
            sys.exit(1)
        #os.chdir(args.i)
        #cur_path=os.getcwd()
        print("工作路径为:",cur_path)
    if args.u or args.recu:# 遍历子目录
        rec_flag = True
        print(f"递归遍历子目录设置为: {rec_flag} \n")    
    if args.b or args.backup:# 备份文件
        backupflg = True
        print(f"自动备份文件设置为: {backupflg} \n")    
    if args.o or args.overwrite:# 冲突时覆盖
        overwrite = True
        print(f"冲突时覆盖设置为: {overwrite} \n")    
    if args.f or args.folder:# 指定目标是文件夹
        folderflag = True
        print(f"指定目标是文件夹: {folderflag} \n") 
    if args.n or args.number:# 指定目标是文件夹
        numberup = args.n if len(args.n)!=0 else args.number 
        print(f"指定数字递增格式: {numberup} \n") 
        
    if args.v or args.v:# 版本号
        print(f"当前操作目录: {cur_path} \n")
        print("版本号: 1.0.0 \n")
    elif args.c or args.case:  # 批量将文件夹名变为大/小写
        my_print(f"当前操作目录: {cur_path}\n")
        case = args.c if len(args.c)!=0 else args.case 
        if case =='lower':
            my_print(f"批量将文件名变为小写")
            if folderflag:
                list = rename_file_to_lowercase(cur_path,True,rec_flag)
            else:
                list = rename_file_to_lowercase(cur_path,True,rec_flag)
        elif case =='upper':
            my_print(f"批量将文件名变为大写")
            if folderflag:
                list = rename_file_to_lowercase(cur_path,False,rec_flag)
            else:
                list = rename_file_to_lowercase(cur_path,False,rec_flag)
        else:
            my_print(f"Argument Error")
    elif args.r or args.replace:  # 将当前目录（directory）的文件名某个字段(str)替换
        my_print(f"当前操作目录: {cur_path}\n")
        my_print(f"将当前目录的文件名去掉 {args.replace} 字段\n")
        replace = args.r if len(args.r)!=0 else args.replace
        if len(replace) > 1:
            if folderflag:
                list = replace_chars_from_foldersname(cur_path,replace[0],replace[1],rec_flag)
            else:
                list = replace_chars_from_filename(cur_path,replace[0],replace[1],rec_flag)
        else:
            if folderflag:
                list = replace_chars_from_foldersname(cur_path,replace[0],"",rec_flag)
            else:
                list = replace_chars_from_filename(cur_path,replace[0],"",rec_flag)
    elif args.a or args.append:  # 将当前目录（directory）的文件名在添加某个字段(str)
        my_print(f"当前操作目录: {cur_path}\n")
        append = args.a if len(args.a)!=0 else args.append
        my_print(f"向当前目录的文件名添加 {args.a} 字段\n")
        if folderflag:
            list = append_chars_to_foldersname(cur_path,append,numberup,rec_flag)
        else:
            list = append_chars_to_filename(cur_path,append,numberup,rec_flag)
    elif args.e or args.exchange:  # 批量将当前目录下，所有指定文件后缀更改为新的后缀）
        my_print(f"当前操作目录: {cur_path}\n")
        exchange = args.e if len(args.e)!=0 else args.exchange

        if len(replace) > 1:
            my_print(f"将当前目录下的文件后缀 {exchange[0]} 更改为 {exchange[1]} \n")
            list = replace_ext_from_filename(cur_path,exchange[0],exchange[1],rec_flag)
        else:
            my_print(f"将当前目录下的文件后缀 {exchange[0]}的后缀名删除 \n")
            list = replace_ext_from_filename(cur_path,exchange,'',rec_flag)
    elif args.s or args.suffix:  # 批量将当前目录下，所有文件后缀更改为新的后缀）
        my_print(f"当前操作目录: {cur_path}\n")
        suffix = args.s if len(args.s)!=0 else args.suffix
        my_print(f"将当前目录下的文件后缀更改为 {suffix} \n")
        list = append_ext_to_filename(cur_path,suffix,rec_flag)
    else: # Help
        print(f"\n当前操作目录: {cur_path}\n")
        parser.print_help()# 如果没有指定任何参数（除了 --help），则默认打印帮助信息
    # 真正操作，重命名文件
    for dir,src,tar,type,operate in list:
        renamed(dir,src,tar,overwrite,backupflg)
        #print(dir,src,tar,type,operate)